package com.gitee.webdav.logger;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.lang.ansi.AnsiColor;
import cn.hutool.core.lang.ansi.AnsiEncoder;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import org.slf4j.Marker;
import org.slf4j.event.Level;
import org.slf4j.helpers.LegacyAbstractLogger;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.function.Function;

/**
 * 格式化日志模板并根据日志级别向控制台输出彩色日志信息
 *
 * @author hongda.li
 */
public class Logger extends LegacyAbstractLogger {
    /**
     * 控制台输出流
     */
    private static final PrintStream PRINT_STREAM = System.out;

    /**
     * 控制台打印时间的颜色
     */
    private static final AnsiColor COLOR_TIME = AnsiColor.WHITE;

    /**
     * 控制台打印类名（日志名称）的颜色
     */
    private static final AnsiColor COLOR_CLASSNAME = AnsiColor.MAGENTA;

    /**
     * 日志级别与日志对应的颜色映射关系
     */
    private static final Function<Level, AnsiColor> COLOR_MAPPING = level -> switch (level) {
        case INFO -> AnsiColor.BRIGHT_CYAN;
        case ERROR -> AnsiColor.BRIGHT_RED;
        case TRACE -> AnsiColor.WHITE;
        case WARN -> AnsiColor.BRIGHT_YELLOW;
        default -> AnsiColor.BRIGHT_BLUE;
    };

    /**
     * 当前日志级别
     */
    private final Level currentLevel;

    public Logger(String name) {
        this.name = name;
        this.currentLevel = LoggerFactory.getLevel();
    }

    @Override
    protected String getFullyQualifiedCallerName() {
        return this.name;
    }

    @Override
    protected void handleNormalizedLoggingCall(Level level, Marker marker, String format, Object[] arguments, Throwable throwable) {
        if (LoggerFactory.isDisabled(this.name)) {
            return;
        }

        // 根据日志级别获取对应的颜色定义
        AnsiColor color = COLOR_MAPPING.apply(level);

        // 格式化特殊编码模板并输出到控制台
        PRINT_STREAM.format(
                AnsiEncoder.encode(
                        COLOR_TIME,
                        "[%s]",
                        color,
                        " [%-5s]%s",
                        color,
                        "[%10s]%s",
                        COLOR_CLASSNAME,
                        "%-45s: ",
                        color,
                        "%s%n"
                ),
                DatePattern.NORM_DATETIME_MS_FORMAT.format(new Date()),
                level.name(),
                " ",
                CharSequenceUtil.subSufByLength(Thread.currentThread().getName(), 10),
                " - ",
                ClassUtil.getShortClassName(this.name),
                CharSequenceUtil.format(format, arguments)
        );

        // 若异常存在则打印到错误流中
        if (throwable != null) {
            PRINT_STREAM.format(AnsiEncoder.encode(COLOR_MAPPING.apply(Level.ERROR), "%s%n"), this.getErrorStackMessage(throwable));
        }
    }

    @Override
    public boolean isTraceEnabled() {
        return this.currentLevel.toInt() <= Level.TRACE.toInt();
    }

    @Override
    public boolean isDebugEnabled() {
        return this.currentLevel.toInt() <= Level.DEBUG.toInt();
    }

    @Override
    public boolean isInfoEnabled() {
        return this.currentLevel.toInt() <= Level.INFO.toInt();
    }

    @Override
    public boolean isWarnEnabled() {
        return this.currentLevel.toInt() <= Level.WARN.toInt();
    }

    @Override
    public boolean isErrorEnabled() {
        return this.currentLevel.toInt() <= Level.ERROR.toInt();
    }

    private String getErrorStackMessage(Throwable throwable) {
        try (ByteArrayOutputStream bs = new ByteArrayOutputStream();
             PrintStream ps = new PrintStream(
                     bs,
                     false,
                     StandardCharsets.UTF_8
             )) {
            throwable.printStackTrace(ps);
            return bs.toString(StandardCharsets.UTF_8);
        } catch (IOException ignored) {
            return throwable.getMessage();
        }
    }
}
